#include "config.h"

#include <sys/types.h>
#include <sys/epoll.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <libgen.h>
#include <ctype.h>
#include <fcntl.h>
#include <libaio.h>
#include <sqlite3.h>
#include <errno.h>
#include <getopt.h>

#define DBG_SUBSYS S_LIBREPLICA

#include "core.h"
#include "cache.h"
#include "disk.h"
#include "sysy_lib.h"
#include "ynet_rpc.h"
#include "job_dock.h"
#include "net_global.h"
#include "diskmd.h"
#include "bh.h"
#include "tpool.h"
#include "lich_md.h"
#include "configure.h"
#include "dbg.h"
#include "replica.h"
#include "core.h"
#include "coroutine.h"
#include "fnotify.h"
#include "locator_rpc.h"

#define __DB_VACUUM_MSG__ "/dev/shm/lich4/msgctl/sub/sqlite_vacuum_msg"
#define __DB_VACUUM_RESULT__ "/dev/shm/lich4/msgctl/sub/sqlite_vacuum_result"

static time_t disk_sqlite3_vacuum_timer;

int disk_sqlite3_vacuum_request(const chkid_t *chkid);

static inline void __generate_fake_chunk(uint64_t id, uint32_t idx, chkid_t *chkid)
{
        chkid->type = __NULL_CHUNK__;
        chkid->id = id;
        chkid->idx = idx;
}

/**
 * note:该函数不应该被频繁调用
 * 且该函数调用期间会影响上层业务
 */
static int disk_sqlite3_vacuum_worker(const char *buf, uint32_t extra)
{
        int ret, i, fd;
        struct timeval t1, t2;
        uint64_t used;
        time_t now = gettime();
        char info[MAX_INFO_LEN];
        chkid_t chkid;

        (void)extra;
        /**
         * note:执行echo 1 > /dev/shm/lich4/msgctl/sqlite_vacuum
         * 会触发两次inotify事件,所以增加下面的判断,来过滤掉一次
         */
        if (now - disk_sqlite3_vacuum_timer >= 5 && strcmp(buf, "1") == 0) {

                unlink(__DB_VACUUM_RESULT__);

                fd = open(__DB_VACUUM_RESULT__, O_CREAT | O_WRONLY, 0755);
                if (fd < 0) {
                        DERROR("open %s failed, errno[%d]\n", __DB_VACUUM_RESULT__, errno);
                }

                for (i = 0; i < __DB_HASH__; i++) {
                        _gettimeofday(&t1, NULL);

                        /**
                         * __db_post_ent里面需要用chkid来确定
                         * db idx, 所以伪造假的chkid
                         */
                        __generate_fake_chunk(i, 0, &chkid);
                        ret = disk_sqlite3_vacuum_request(&chkid);

                        _gettimeofday(&t2, NULL);
                        used = _time_used(&t1, &t2);
                        if (ret == SQLITE_OK) {
                                snprintf(info, MAX_INFO_LEN, "[%d] sqlite vacuum success, used %lums, ret: %d...\n", i,
                                                used / 1000, ret);
                        } else {
                                snprintf(info, MAX_INFO_LEN, "[%d] sqlite vacuum failed, used %lums, ret: %d...\n", i,
                                                used / 1000, ret);
                        }

                        DINFO("%s", info);
                        if (fd >= 0)
                                write(fd, info, strlen(info)+1);
                }

                if (fd >= 0)
                        close(fd);

                disk_sqlite3_vacuum_timer = gettime();
        } else {
                DWARN("sqlite vacuum operation is too frequent...\n");
        }

        return 0;
}

int disk_sqlite3_vacuum_init()
{
        disk_sqlite3_vacuum_timer = gettime();

        unlink(__DB_VACUUM_RESULT__);

        return fnotify_create(__DB_VACUUM_MSG__, "0", disk_sqlite3_vacuum_worker, 0);
}
